Utforska avancerade TypeScript-funktioner som mallbokstavliga typer och villkorliga typer för att skriva mer uttrycksfull och underhÄllbar kod. BemÀstra typmanipulation för komplexa scenarier.
TypeScript Avancerade Typer: BemÀstra Mallbokstavliga och Villkorliga Typer
Typerkripts styrka ligger i dess kraftfulla typsystem. Medan grundlÀggande typer som string, number och boolean Àr tillrÀckliga för mÄnga scenarier, lÄser avancerade funktioner som mallbokstavliga typer och villkorliga typer upp en ny nivÄ av uttrycksfullhet och typsÀkerhet. Den hÀr guiden ger en omfattande översikt över dessa avancerade typer och utforskar deras möjligheter och demonstrerar praktiska tillÀmpningar.
FörstÄ Mallbokstavliga Typer
Mallbokstavliga typer bygger pÄ JavaScripts mallbokstavliga uttryck, vilket gör att du kan definiera typer baserade pÄ strÀnginterpolering. Detta möjliggör skapandet av typer som representerar specifika strÀngmönster, vilket gör din kod mer robust och förutsÀgbar.
GrundlÀggande Syntax och AnvÀndning
Mallbokstavliga typer anvĂ€nder backticks (`) för att omsluta typdefinitionen, liknande JavaScripts mallbokstavliga uttryck. Inom backticks kan du interpolera andra typer med syntaxen ${}. Det Ă€r hĂ€r magin sker â du skapar i grunden en typ som Ă€r en strĂ€ng, konstruerad vid kompilering baserat pĂ„ typerna inuti interpoleringen.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIEndpoint = `/api/${string}`;
// Exempel pÄ anvÀndning
const getEndpoint: APIEndpoint = "/api/users"; // Giltigt
const postEndpoint: APIEndpoint = "/api/products/123"; // Giltigt
const invalidEndpoint: APIEndpoint = "/admin/settings"; // TypeScript visar inget fel hÀr eftersom `string` kan vara vad som helst
I det hĂ€r exemplet Ă€r APIEndpoint en typ som representerar vilken strĂ€ng som helst som börjar med /api/. Ăven om det hĂ€r grundlĂ€ggande exemplet Ă€r anvĂ€ndbart, framtrĂ€der den verkliga kraften hos mallbokstavliga typer nĂ€r de kombineras med mer specifika typbegrĂ€nsningar.
Kombinera med Unions-typer
Mallbokstavliga typer lyser verkligen nÀr de anvÀnds med unions-typer. Detta gör att du kan skapa typer som representerar en specifik uppsÀttning strÀngkombinationer.
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type APIPath = "users" | "products" | "orders";
type APIEndpoint = `/${APIPath}/${HTTPMethod}`;
// Giltiga API-slutpunkter
const getUsers: APIEndpoint = "/users/GET";
const postProducts: APIEndpoint = "/products/POST";
// Ogiltiga API-slutpunkter (kommer att resultera i TypeScript-fel)
// const invalidEndpoint: APIEndpoint = "/users/PATCH"; // Fel: "/users/PATCH" Àr inte tilldelningsbar till typen "/users/GET" | "/users/POST" | "/users/PUT" | "/users/DELETE" | "/products/GET" | "/products/POST" | ... 3 mer ... | "/orders/DELETE".
Nu Àr APIEndpoint en mer restriktiv typ som endast tillÄter specifika kombinationer av API-sökvÀgar och HTTP-metoder. TypeScript kommer att flagga alla försök att anvÀnda ogiltiga kombinationer, vilket förbÀttrar typsÀkerheten.
StrÀngmanipulation med Mallbokstavliga Typer
TypeScript tillhandahÄller inneboende strÀngmanipulationstyper som fungerar sömlöst med mallbokstavliga typer. Dessa typer gör att du kan transformera strÀngar vid kompilering.
- Uppercase: Konverterar en strÀng till versaler.
- Lowercase: Konverterar en strÀng till gemener.
- Capitalize: Versaliserar den första bokstaven i en strÀng.
- Uncapitalize: Konverterar den första bokstaven i en strÀng till gemener.
type Greeting = "hello world";
type UppercaseGreeting = Uppercase; // "HELLO WORLD"
type LowercaseGreeting = Lowercase; // "hello world"
type CapitalizedGreeting = Capitalize; // "Hello world"
type UncapitalizedGreeting = Uncapitalize; // "hello world"
Dessa strÀngmanipulationstyper Àr sÀrskilt anvÀndbara för att automatiskt generera typer baserat pÄ namngivningskonventioner. Du kan till exempel hÀrleda ÄtgÀrdstyper frÄn hÀndelsenamn eller vice versa.
Praktiska TillÀmpningar av Mallbokstavliga Typer
- API-slutpunktsdefinition: Som demonstreras ovan, definiera API-slutpunkter med exakta typbegrÀnsningar.
- HÀndelsehantering: Skapa typer för hÀndelsenamn med specifika prefix och suffix.
- Generering av CSS-klasser: Generera CSS-klassnamn baserat pÄ komponentnamn och tillstÄnd.
- Byggande av databasfrÄgor: SÀkerstÀll typsÀkerhet vid konstruktion av databasfrÄgor.
Internationellt Exempel: Valutainmatning
FörestÀll dig att bygga en finansiell applikation som stöder flera valutor. Du kan anvÀnda mallbokstavliga typer för att tvinga fram korrekt valutainmatning.
type CurrencyCode = "USD" | "EUR" | "GBP" | "JPY";
type CurrencyFormat = `${number} ${T}`;
const priceUSD: CurrencyFormat<"USD"> = "100 USD"; // Giltigt
const priceEUR: CurrencyFormat<"EUR"> = "50 EUR"; // Giltigt
// const priceInvalid: CurrencyFormat<"USD"> = "100 EUR"; // Fel: Typen 'string' Àr inte tilldelningsbar till typen '`${number} USD`'.
function formatCurrency(amount: number, currency: T): CurrencyFormat {
return `${amount} ${currency}`;
}
const formattedUSD = formatCurrency(250, "USD"); // Typ: "250 USD"
const formattedEUR = formatCurrency(100, "EUR"); // Typ: "100 EUR"
Det hÀr exemplet sÀkerstÀller att valutavÀrden alltid matas in med rÀtt valutakod, vilket förhindrar potentiella fel.
Fördjupning i Villkorliga Typer
Villkorliga typer introducerar grenlogik i Typerkripts typsystem, vilket gör att du kan definiera typer som Àr beroende av andra typer. Denna funktion Àr otroligt kraftfull för att skapa mycket flexibla och ÄteranvÀndbara typdefinitioner.
GrundlÀggande Syntax och AnvÀndning
Villkorliga typer anvÀnder nyckelordet infer och ternÀra operatorn (condition ? trueType : falseType) för att definiera typvillkor.
type IsString = T extends string ? true : false;
type StringCheck = IsString; // typ StringCheck = true
type NumberCheck = IsString; // typ NumberCheck = false
I det hÀr exemplet Àr IsString en villkorlig typ som kontrollerar om T kan tilldelas string. Om den kan det, löses typen till true; annars löses den till false.
Nyckelordet infer
Nyckelordet infer gör att du kan extrahera en typ frÄn en typ. Detta Àr sÀrskilt anvÀndbart nÀr du arbetar med komplexa typer som funktionstyper eller arraytyper.
type ReturnType any> = T extends (...args: any) => infer R ? R : any;
function add(a: number, b: number): number {
return a + b;
}
type AddReturnType = ReturnType; // typ AddReturnType = number
I det hÀr exemplet extraherar ReturnType returtypen för en funktionstyp T. Delen infer R i den villkorliga typen hÀrleder returtypen och tilldelar den till typvariabeln R. Om T inte Àr en funktionstyp, löses typen till any.
Distributiva Villkorliga Typer
Villkorliga typer blir distributiva nÀr den kontrollerade typen Àr en naken typ-parameter. Detta innebÀr att den villkorliga typen tillÀmpas pÄ varje medlem av unionstypen separat.
type ToArray = T extends any ? T[] : never;
type NumberOrStringArray = ToArray; // typ NumberOrStringArray = string[] | number[]
I det hÀr exemplet konverterar ToArray en typ T till en arraytyp. Eftersom T Àr en naken typ-parameter (inte omsluten av en annan typ), tillÀmpas den villkorliga typen separat pÄ number och string, vilket resulterar i en union av number[] och string[].
Praktiska TillÀmpningar av Villkorliga Typer
- Extrahera Returtyper: Som demonstreras ovan, extrahera returtypen för en funktion.
- Filtrera Typer frÄn en Union: Skapa en typ som endast innehÄller specifika typer frÄn en union.
- Definiera Ăverlagrade Funktionstyper: Skapa olika funktionstyper baserat pĂ„ indatatyper.
- Skapa Typvakter: Definiera funktioner som smalnar av typen av en variabel.
Internationellt Exempel: Hantera Olika Datumformat
Olika regioner i vÀrlden anvÀnder olika datumformat. Du kan anvÀnda villkorliga typer för att hantera dessa variationer.
type DateFormat = "YYYY-MM-DD" | "MM/DD/YYYY" | "DD.MM.YYYY";
type ParseDate = T extends "YYYY-MM-DD"
? { year: number; month: number; day: number; format: "YYYY-MM-DD" }
: T extends "MM/DD/YYYY"
? { month: number; day: number; year: number; format: "MM/DD/YYYY" }
: T extends "DD.MM.YYYY"
? { day: number; month: number; year: number; format: "DD.MM.YYYY" }
: never;
function parseDate(dateString: string, format: T): ParseDate {
// (Implementeringen skulle hantera olika datumformat)
if (format === "YYYY-MM-DD") {
const [year, month, day] = dateString.split("-").map(Number);
return { year, month, day, format } as ParseDate;
} else if (format === "MM/DD/YYYY") {
const [month, day, year] = dateString.split("/").map(Number);
return { month, day, year, format } as ParseDate;
} else if (format === "DD.MM.YYYY") {
const [day, month, year] = dateString.split(".").map(Number);
return { day, month, year, format } as ParseDate;
} else {
throw new Error("Ogiltigt datumformat");
}
}
const parsedDateISO = parseDate("2023-10-27", "YYYY-MM-DD"); // Typ: { year: number; month: number; day: number; format: "YYYY-MM-DD"; }
const parsedDateUS = parseDate("10/27/2023", "MM/DD/YYYY"); // Typ: { month: number; day: number; year: number; format: "MM/DD/YYYY"; }
const parsedDateEU = parseDate("27.10.2023", "DD.MM.YYYY"); // Typ: { day: number; month: number; year: number; format: "DD.MM.YYYY"; }
console.log(parsedDateISO.year); // Ă
tkomst till Äret med vetskap om att det kommer att finnas dÀr
Det hÀr exemplet anvÀnder villkorliga typer för att definiera olika datumparsningfunktioner baserat pÄ det angivna datumformatet. Typen ParseDate sÀkerstÀller att det returnerade objektet har rÀtt egenskaper baserat pÄ formatet.
Kombinera Mallbokstavliga och Villkorliga Typer
Den verkliga kraften kommer nÀr du kombinerar mallbokstavliga typer och villkorliga typer. Detta möjliggör otroligt kraftfulla typmanipulationer.
type EventName = `on${Capitalize}`;
type ExtractEventPayload = T extends EventName
? { type: T; payload: any } // Förenklat för demonstration
: never;
type ClickEvent = EventName<"click">; // "onClick"
type MouseOverEvent = EventName<"mouseOver">; // "onMouseOver"
// Exempel pÄ funktion som tar en typ
function processEvent(event: T): ExtractEventPayload {
// I en verklig implementation skulle vi faktiskt skicka ut hÀndelsen.
console.log(`Behandlar hÀndelse ${event}`);
// I en verklig implementation skulle payloaden baseras pÄ hÀndelsetyp.
return { type: event, payload: {} } as ExtractEventPayload;
}
// Notera att retartyperna Àr mycket specifika:
const clickEvent = processEvent("onClick"); // { type: "onClick"; payload: any; }
const mouseOverEvent = processEvent("onMouseOver"); // { type: "onMouseOver"; payload: any; }
// Om du anvÀnder andra strÀngar fÄr du never:
// const someOtherEvent = processEvent("someOtherEvent"); // Typen Àr `never`
BĂ€sta Praxis och ĂvervĂ€ganden
- HĂ„ll det enkelt: Ăven om dessa avancerade typer Ă€r kraftfulla kan de snabbt bli komplexa. StrĂ€va efter tydlighet och underhĂ„llbarhet.
- Testa Noggrant: SÀkerstÀll att dina typdefinitioner fungerar som förvÀntat genom att skriva omfattande enhetstester.
- Dokumentera din kod: Dokumentera tydligt syftet och beteendet hos dina avancerade typer för att förbÀttra kodlÀsbarheten.
- ĂvervĂ€g prestanda: Ăverdriven anvĂ€ndning av avancerade typer kan pĂ„verka kompileringstiden. Profilera din kod och optimera dĂ€r det behövs.
Slutsats
Mallbokstavliga typer och villkorliga typer Àr kraftfulla verktyg i Typerkripts arsenal. Genom att bemÀstra dessa avancerade typer kan du skriva mer uttrycksfull, underhÄllbar och typsÀker kod. Dessa funktioner gör det möjligt för dig att fÄnga komplexa relationer mellan typer, tvinga fram striktare begrÀnsningar och skapa mycket ÄteranvÀndbara typdefinitioner. Omfamna dessa tekniker för att höja dina Typerkript-fÀrdigheter och bygga robusta och skalbara applikationer för en global publik.